package com.ctg.behavior.calc.impala.calculator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.ctg.behavior.api.CtCalcStatus;
import com.ctg.behavior.calc.common.CtSQLParam;
import com.ctg.behavior.calc.impala.CtCalculator;
import com.ctg.behavior.calc.impala.builder.funnel.CtFunnelStepUserIdsReprotBuilder;
import com.ctg.behavior.calc.impala.builder.retentions.CtRetentionsUserIdsReprotBuilder;
import com.ctg.behavior.calc.impala.builder.userlist.CtFunnelExtCalcBodyBuilder;
import com.ctg.behavior.calc.impala.builder.userlist.CtRetentionsExtCalcBodyBuilder;
import com.ctg.behavior.calc.impala.builder.userlist.CtUGULReportBuilder;
import com.ctg.behavior.calc.impala.builder.userlist.CtULCalcResultBuilder;
import com.ctg.behavior.calc.impala.engine.ImpalaSQLEngine;
import com.ctg.behavior.calc.impala.report.funnel.CtCalcFunnelStepUserIdsReport;
import com.ctg.behavior.calc.impala.report.retentions.CtRetentionsUserIdsReport;
import com.ctg.behavior.calc.impala.report.userlist.CtCalcUGULReport;
import com.ctg.behavior.calc.impala.transfer.CtTotalTransfer;
import com.ctg.behavior.calc.impala.transfer.retentions.CtRetentionsUserIdsTransfer;
import com.ctg.behavior.calc.impala.transfer.userlist.CtFunnelUserListTransfer;
import com.ctg.behavior.calc.impala.transfer.userlist.CtUGUserListTransfer;
import com.ctg.behavior.calc.impala.transfer.userlist.CtUserListTransfer;
import com.ctg.behavior.calc.impala.utils.CtCalculatorUtils;
import com.ctg.behavior.client.impala.common.CtQueryResult;
import com.ctg.behavior.common.calc.CtCalcBody;
import com.ctg.behavior.common.calc.CtCalcResult;
import com.ctg.behavior.common.calc.CtCalcTotal;
import com.ctg.behavior.common.consts.CtCalcTaskConsts;
import com.ctg.behavior.common.consts.CtFieldConsts;
import com.ctg.behavior.common.funnel.CtFunnelSliceBody;
import com.ctg.behavior.common.retentions.CtRetentionsSliceBody;
import com.ctg.behavior.common.userslist.CtULCalcBody;
import com.ctg.behavior.common.userslist.CtULCalcResult;
import com.ctg.behavior.utils.CtDateUtils;
import com.ctg.behavior.utils.CtStatusUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class CtULCalculator implements CtCalculator {

	@Autowired
	private ImpalaSQLEngine sqlEngine;
	
	// 用户分群 - 用户列表
	@Autowired
	private CtUGUserListTransfer ugUserListTransfer;
	@Autowired
	private CtTotalTransfer ctTotalTransfer;
	@Autowired
	private CtUGULReportBuilder ugulReportBuilder;
	
	// 漏斗分析 - 用户列表
	@Autowired
	private CtFunnelExtCalcBodyBuilder funnelExtCalcBodyBuilder;
	@Autowired
	private CtFunnelStepUserIdsReprotBuilder funnelStepUserIdsReprotBuilder;
//	@Autowired
//	private CtFunnelStepUserIdsTransfer funnelStepUserIdsTransfer;
	@Autowired
	private CtFunnelUserListTransfer funnelUserListTransfer;
	
	// 留存分析 - 用户列表
	@Autowired
	private CtRetentionsExtCalcBodyBuilder retentionsExtCalcBodyBuilder;
	@Autowired
	private CtRetentionsUserIdsReprotBuilder retentionsUserIdsReprotBuilder;
	@Autowired
	private CtRetentionsUserIdsTransfer retentionsUserIdsTransfer;
	
	@Autowired
	private CtUserListTransfer userListTransfer;

	// 用户列表查询结果组装返回报文
	@Autowired
	private CtULCalcResultBuilder resultBuilder;

	/****************************************************************
	 * @apiNote: 1.
	 * @apiNote: 2.
	 */
	@Override
	public CtCalcResult doCalculator(CtCalcBody calcBody) {
		CtULCalcResult calcResult = new CtULCalcResult();
		if (!(calcBody instanceof CtULCalcBody)) {
			log.error("calcBody={}", calcBody.getClass());
			CtStatusUtils.fillStatus(calcResult, CtCalcStatus.BodyNoMatch);
			return calcResult;
		}

		log.info("time - start: {}", CtDateUtils.getCurrentDateSSS());
		CtULCalcBody ulCalcBody = (CtULCalcBody) calcBody;
		log.info("ulCalcBody={}", JSON.toJSONString(ulCalcBody));

		if (CtCalcTaskConsts.TASK_TYPE_USERGROUPS.equals(ulCalcBody.getUpperTaskType())) {
			// 用户分群 之 用户列表
			return this.doUGULCalculator(ulCalcBody);
		} else if (CtCalcTaskConsts.TASK_TYPE_FUNNEL.equals(ulCalcBody.getUpperTaskType())) {
			// 漏斗分析 之 用户列表
			return this.doFunnelULCalculator(ulCalcBody);
		} else if (CtCalcTaskConsts.TASK_TYPE_RETENTION.equals(ulCalcBody.getUpperTaskType())) {
			// 留存分析 之 用户列表
			return this.doRetentionULCalculator(ulCalcBody);
		}

		log.info("time - end: {}", CtDateUtils.getCurrentDateSSS());

		return calcResult;
	}

	/****************************************************************
	 * 用户分群 ===> 用户列表
	 * 
	 * @param ulCalcBody
	 * @return
	 */
	private CtCalcResult doUGULCalculator(CtULCalcBody ulCalcBody) {
		CtULCalcResult calcResult = new CtULCalcResult();
		// 用户分群 之 用户列表
		CtCalcUGULReport ugulReport = ugulReportBuilder.doBuilder(ulCalcBody);
		log.info("ugulReport={}", JSON.toJSONString(ugulReport));
		String impalaSQL = ugUserListTransfer.doTransfer(ugulReport, CtSQLParam.createParam());
		CtQueryResult queryResult = sqlEngine.doQuery("", impalaSQL);
		log.info("queryResult={}", JSON.toJSONString(queryResult));

		CtCalculatorUtils.fillCalcResult(calcResult, queryResult);
		if (!calcResult.getSuccess()) {
			log.error(queryResult.getCode());
			return calcResult;
		}

		calcResult = resultBuilder.doBuilder(calcResult, queryResult.getRows(), ulCalcBody);

		// 把用户列的总数计算出来
		if (!queryResult.getRows().isEmpty()) {
			CtCalcTotal calcTotal = new CtCalcTotal();
			calcTotal.setDetailSQL(impalaSQL);
			String totalSQL = ctTotalTransfer.doTransfer(calcTotal, CtSQLParam.createParam());
			CtQueryResult totalResult = sqlEngine.doQuery("", totalSQL);
			log.info("totalResult={}", JSON.toJSONString(totalResult));
			Integer countTotal = Integer.valueOf(totalResult.getRows().get(0).get(CtFieldConsts.FIELD_KEY_TOTAL).toString());
			if (ulCalcBody.getLimit() <= 0 || ulCalcBody.getLimit() > countTotal) {
				calcResult.getValue().setTotal(countTotal);
			} else {
				calcResult.getValue().setTotal(ulCalcBody.getLimit());
			}
		}
		return calcResult;
	}

	/****************************************************************
	 * 漏斗分析 ===> 用户列表
	 * 
	 * @param ulCalcBody
	 * @return
	 */
	private CtCalcResult doFunnelULCalculator(CtULCalcBody ulCalcBody) {
		CtULCalcResult calcResult = new CtULCalcResult();
		// 漏斗分析 ===> 用户列表
		CtFunnelSliceBody funnelExtCalcBody = funnelExtCalcBodyBuilder.doBuilder(ulCalcBody);
		CtCalcFunnelStepUserIdsReport funnelStepUserIdsReport = funnelStepUserIdsReprotBuilder.doBuilder(funnelExtCalcBody);
		log.info("funnelStepUserIdsReport={}", JSON.toJSONString(funnelStepUserIdsReport));

		String userIdsSQL = userListTransfer.doTransfer(funnelStepUserIdsReport, CtSQLParam.createParam());
		CtSQLParam sqlParam = CtSQLParam.createFilterParam(userIdsSQL);
		String userListSQL = funnelUserListTransfer.doTransfer(ulCalcBody, sqlParam);
		String queryType = CtCalcTaskConsts.TASK_TYPE_USERSLIST + "@FUNNEL";
		CtQueryResult queryResult = sqlEngine.doQuery(queryType, userListSQL);
		log.info("queryResult={}", JSON.toJSONString(queryResult));

		CtCalculatorUtils.fillCalcResult(calcResult, queryResult);
		if (!calcResult.getSuccess()) {
			log.error(queryResult.getCode());
			return calcResult;
		}

		calcResult = resultBuilder.doBuilder(calcResult, queryResult.getRows(), ulCalcBody);

		// 把用户列的总数计算出来
		if (!queryResult.getRows().isEmpty()) {
			CtCalcTotal calcTotal = new CtCalcTotal();
			calcTotal.setDetailSQL(userIdsSQL);
			String totalSQL = ctTotalTransfer.doTransfer(calcTotal, CtSQLParam.createParam());
			queryType = CtCalcTaskConsts.TASK_TYPE_USERSLIST + "@FUNNEL@TOTAL";
			CtQueryResult totalResult = sqlEngine.doQuery(queryType, totalSQL);
			log.info("totalResult={}", JSON.toJSONString(totalResult));
			Integer countTotal = Integer.valueOf(totalResult.getRows().get(0).get(CtFieldConsts.FIELD_KEY_TOTAL).toString());
			if (ulCalcBody.getLimit() <= 0 || ulCalcBody.getLimit() > countTotal) {
				calcResult.getValue().setTotal(countTotal);
			} else {
				calcResult.getValue().setTotal(ulCalcBody.getLimit());
			}
		}
		return calcResult;
	}

	/****************************************************************
	 * 留存分析 ===> 用户列表
	 * 
	 * @param ulCalcBody
	 * @return
	 */
	private CtCalcResult doRetentionULCalculator(CtULCalcBody ulCalcBody) {
		CtULCalcResult calcResult = new CtULCalcResult();
		// 漏斗分析 ===> 用户列表
		CtRetentionsSliceBody funnelExtCalcBody = retentionsExtCalcBodyBuilder.doBuilder(ulCalcBody);
		CtRetentionsUserIdsReport funnelStepUserIdsReport = retentionsUserIdsReprotBuilder.doBuilder(funnelExtCalcBody);
		log.info("funnelStepUserIdsReport={}", JSON.toJSONString(funnelStepUserIdsReport));

		String userIdsSQL = retentionsUserIdsTransfer.doTransfer(funnelStepUserIdsReport, CtSQLParam.createParam());
		CtSQLParam sqlParam = CtSQLParam.createFilterParam(userIdsSQL);
		String userListSQL = userListTransfer.doTransfer(ulCalcBody, sqlParam);
		String queryType = CtCalcTaskConsts.TASK_TYPE_USERSLIST + "@RETENTION";
		CtQueryResult queryResult = sqlEngine.doQuery(queryType, userListSQL);
		log.info("queryResult={}", JSON.toJSONString(queryResult));

		CtCalculatorUtils.fillCalcResult(calcResult, queryResult);
		if (!calcResult.getSuccess()) {
			log.error(queryResult.getCode());
			return calcResult;
		}

		calcResult = resultBuilder.doBuilder(calcResult, queryResult.getRows(), ulCalcBody);

		// 把用户列的总数计算出来
		if (!queryResult.getRows().isEmpty()) {
			CtCalcTotal calcTotal = new CtCalcTotal();
			calcTotal.setDetailSQL(userIdsSQL);
			String totalSQL = ctTotalTransfer.doTransfer(calcTotal, CtSQLParam.createParam());
			queryType = CtCalcTaskConsts.TASK_TYPE_USERSLIST + "@RETENTION@TOTAL";
			CtQueryResult totalResult = sqlEngine.doQuery(queryType, totalSQL);
			log.info("totalResult={}", JSON.toJSONString(totalResult));
			Integer countTotal = Integer.valueOf(totalResult.getRows().get(0).get(CtFieldConsts.FIELD_KEY_TOTAL).toString());
			if (ulCalcBody.getLimit() <= 0 || ulCalcBody.getLimit() > countTotal) {
				calcResult.getValue().setTotal(countTotal);
			} else {
				calcResult.getValue().setTotal(ulCalcBody.getLimit());
			}
		}
		return calcResult;
	}

}
